#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <stdlib.h>
#include <error.h>
#include "tlv.h"
#include "crc-itu-t.h"
#include "message_callback.h"

/*
* 功能：    入TLV数据封装成TLV报
* Tag：     TLV当中的tag；
* len:      TLV当中value的长度；
* src_buf:  TLV当中value值；
* 返回值：  联合指针；
*/
union_s *tlv_pack(unsigned char Tag, uint8_t len, char *src_buf)
{
    int         i; 
    union_s     *tlv_union;
    //考虑结构体对齐
    tlv_union = (union_s *)malloc(sizeof(tlv_s)+sizeof(char)*len); 
    if (tlv_union == NULL)
    {
        printf("malloc failure, %d\n", __LINE__ );
        return NULL;
    }
    if (len < 0)
    {
        return NULL;
    }
    tlv_union->tlv.size = len+2;    //TLV的总长度
    tlv_union->tlv.tag = Tag;
    tlv_union->tlv.len = len;
    
    print_test; 
    for (i=0; i < len; ++i) 
    {   
        tlv_union->tlv.val[i] = src_buf[i];    //联合共享内存空间
        //printf("%02x\n", tlv_union->buf[i] );
    }
    printf("Data pack TLV OK.\n");
    return tlv_union;       //返回value的长度
}



/* 
 * 功能：       TLV解包；
 * src_buf:     待解包的源buf；
 * size：       待解包的源buf大小；
 * tlv_val:     存放解析的id，温度和时间的TLV数据;
 * tlv_flag:    id，time和temper的标识;
 * 返回值：     小于0解析出错，否则解析成功;
*/
int tlv_unpack(unsigned char *src_buf, int *left_byte, tlv_data *tlv_val, int* tlv_flag)
{

    int             i;
    int             j;
    int             len;
    unsigned short  val;
    unsigned short  crc16;
    unsigned char   *ptr;
    int             ofset = 0;
    int             size = *left_byte;

    if (!src_buf)
    {
        printf("Invailed input!\n");
        return -1;
    }

    print_test;
    print_buf(src_buf, size);
    
    //在一个buf中一帧一帧解析TLV，进行读取数据
    for (i=0; i<size; i++)
    {
        //首先判断读取的全部帧当中，是否存在完整TLV数据 
        if (size < MIN_PACK_SIZE)
        {
             printf("Pack too short and get data stream failure.\n");
             *left_byte = size;
             return *left_byte;     //返回剩余数据大小 
        }
        print_test;
        //printf("i=%d, size=%d\n", i, size);
        //读到报文头
        //print_buf(src_buf, (size);
        if (src_buf[i] == (unsigned char)HEADER )
        {
            //print_test;
            //print_buf(src_buf, (size); 
            printf("Find Header.\n");
            //只有帧头和标志位,没有value值 (读到buf的最后了)
            if (size-i <= 2)
            {
                printf("remain data too short and read ok.\n");
                printf("Wait continue input data.\n");

                //这里需要注意, 若将字符串后面的'\0'也读取到了,那么会将目的buf的后面数据全部清除
                memmove(src_buf, &src_buf[i], size-i);         //数据拷贝到buf首部，等待下一次传输数据，再读取
                *left_byte = size-i;            //改变实参的值
                return  *left_byte;             //剩余字节数
            }

            ptr = &src_buf[i];  //找到TLV的首地址
            len = ptr[2];
            
            //帧(length)长度错误，直接丢弃该报文段，并发送NAK报文，请求重新发数据
            if ( len < MIN_VALUE_SIZE || len > MAX_PACK_SIZE)
            {
                printf("Current TLV data abnormal.\n");
                memmove(src_buf, &ptr[2], size-i-2);    //上面已判断剩余两个数据情况，第三个数据出错，跳过这一字节
                size = size-i-2;    
                i = -1;              //for循环中continue结束本次循环，会执行i++，进行下一次循环
                continue;           //已将剩余数据全部放在src_buf的首地址了
            }
            //报文未读完，继续保存到buf,下一次读取
            if(len > size-i)
            {
                memmove(src_buf, ptr, size-i);
                printf("TLV packed is accomplish.\n");
                printf("Wait next data input.\n");
                *left_byte = size-i;
                return *left_byte;    //剩余字节数
            }
            
            printf("Start CRC checkout.\n");

            //正常读取TLV数据包,需要首先判断CRC检验和是否相同, 不同则直接丢弃该报文段
            crc16 = crc_itu_t(MAGIC_CRC, (unsigned char *)ptr, len+FIX_PACK_SIZE-2);  //获得unsigned short类型校验位
            //printf("%04x \n", crc16);
            print_test;
            //将CRC的2字节的校验值，转化成两个一字节存在unsigned char当中 
            val = bytes_to_ushort(&ptr[len+FIX_PACK_SIZE-2], 2); 
            if (crc16 != val)
            {
                printf("CRC checkout failure.\n");
                memmove(src_buf, &ptr[len+FIX_PACK_SIZE], size-i-len-FIX_PACK_SIZE);
                size = size-i-len-FIX_PACK_SIZE;    //丢弃当前TLV
                i = -1;
                continue;
            }
            printf("CRC checkout sucessfuly.\n");

            //解析成功,将该TLV数据包保存进行处理
            print_test;
            if (ptr[1] == Tag_id)
            {
                len = ptr[2];       //TLV中len的地址
                ofset = 3;          //TLV中value的地址
                //printf("ofset = %d, tag=%02x\n",  ofset, ptr[1]);
                for (j=0; j<len; j++)
                {
                    tlv_val->id[j] = ptr[ofset];                                    
                    ++ofset;
                }
                //增加标识，看三个TLV是否都解析成功，否则直接请求重传
                *tlv_flag = 1;
                print_buf(tlv_val->id, len);
                print_test;

            }
            else if(ptr[1] == Tag_time)
            {
                len = ptr[2];
                ofset = 3;
                //printf("ofset = %d, tag=%02x\n",  ofset, ptr[1]);
                for (j=0; j<len; j++)
                {
                    tlv_val->time[j] = ptr[ofset];                                    
                    ++ofset;
                }
                (*tlv_flag) |= (*tlv_flag) <<1;       //从低位开始，将第二位置1，再和第一个位或运算（0001|0010 = 0011）
                print_buf(tlv_val->time, len);
                print_test; 
            }
            else if(ptr[1] == Tag_temper)
            {
                len = ptr[2];
                ofset = 3;
                //printf("ofset = %d, tag=%02x\n",  ofset, ptr[1]);
                for (j=0; j<len; j++)
                {
                    tlv_val->temper[j] = ptr[ofset];                                    
                    ++ofset;
                }
                (*tlv_flag) |= (*tlv_flag) <<1;
                print_buf(tlv_val->temper, len);
                print_test;
            }
            else 
            {
                print_test;
                return -1;
            }
            //解析完当前TLV，丢弃该数据，进行下一个TLV解析
            memmove(src_buf, &ptr[len+FIX_PACK_SIZE], size-i-len-FIX_PACK_SIZE);
            size = size-i-len-FIX_PACK_SIZE;    //解析剩余TLV数据
            print_test;
            i = -1;                  //从0开始重新遍历
            continue;

        }//if(ptr[i] == HEARDER)
    }//for
}



